Skip to main content
Glama

CJ-MCP

by PoivronMax
仓颉函数调用流程详解.md19.3 kB
# 增量引擎:函数调用流程详解 > 本文档详细追踪增量引擎中关键函数的调用链,特别关注 lambda 表达式的创建时机与执行时机。 **基于**: Cangjie/KoalaRuntime 实现 **核心文件**: `runtime/src/core/StateManagerImpl.cj`, `runtime/src/core/ScopeImpl.cj`, `runtime/src/core/memo.cj` --- ## 目录 1. [memoRoot 创建阶段完整流程](#1-memoroot-创建阶段完整流程) 2. [getValue 触发时的完整流程](#2-getvalue-触发时的完整流程) 3. [Lambda 表达式执行时机总结](#3-lambda-表达式执行时机总结) --- ## 1. memoRoot 创建阶段完整流程 ### 1.1 调用链概览 ``` 用户代码: memoRoot(rootNode, update) ↓ memoRoot(node, update) ↓ GlobalStateManager.instance() ↓ StateManagerImpl.updatableNode(node, lambda1, None) ↓ ScopeImpl.init(...) [创建根作用域,传入 lambda2 作为 compute] ↓ 返回 ComputableState<Node>(尚未执行计算) ``` ### 1.2 逐步执行分析 #### 步骤 1: memoRoot 被调用 ```29:32:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj public func memoRoot<Node>(node: Node, update: (StateManagerImpl) -> Unit): ComputableState<Node> where Node <: IncrementalNode { let manager = GlobalStateManager.instance() manager.updatableNode(node, { => manager.runWithFrozen(update)}, None) } ``` **执行内容**: 1. `GlobalStateManager.instance()` → 获取或创建 `StateManagerImpl` 2. **创建 lambda1**: `{ => manager.runWithFrozen(update) }` - **注意**: 此时 lambda 只是被创建,**尚未执行** - `update` 参数被捕获在闭包中 3. 调用 `updatableNode(node, lambda1, None)` **返回**: `ComputableState<Node>`(根作用域,但此时尚未计算) --- #### 步骤 2: updatableNode 被调用 ```165:184:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj public func updatableNode<Node>(node: Node, update: () -> Unit, cleanup: ?() -> Unit): ComputableState<Node> where Node <: IncrementalNode { this.checkForStateComputing() let scope = ScopeImpl<Node>( None, 2, { => update() node }, {_ => cleanup?()}, None ) scope.manager = this scope.nodeAttached = node scope.nodeRef = node scope.param(0, {=> dumpHierarchyOf(scope)}, CONTEXT_ROOT_SCOPE_HIERARCHY_SUPPLIER, true) scope.param(1, {=> dumpHierarchyOf(node)}, CONTEXT_ROOT_NODE_HIERARCHY_SUPPLIER, true) return scope } ``` **执行内容**: 1. `checkForStateComputing()` → 检查是否在计算中(不应在计算中创建根节点) 2. **创建 lambda2**: `{ => update(); node }` - 捕获了 `update`(即 lambda1)和 `node` - **注意**: 此时 lambda2 只是被创建,**尚未执行** 3. **创建 lambda3**: `{_ => cleanup?()}` - 捕获了 `cleanup`(这里是 `None`) - **注意**: 此时 lambda3 只是被创建,**尚未执行** 4. **调用 `ScopeImpl.init(None, 2, lambda2, lambda3, None)`**: ```353:358:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj init(id: ?Hashscopeid, paramCount: Int64, compute: ?() -> Value, cleanup: ?(value: ?Value) -> Unit, reuseKey: ?String) { super(id, paramCount) this.myCompute = compute // 保存 lambda2,但不执行 this.myCleanup = cleanup // 保存 lambda3,但不执行 this.myreuseKey = reuseKey } ``` - `super(id, paramCount)` → 初始化父类 `ManagedScope` - `this.myCompute = lambda2` → 保存 lambda,**不执行** - `this.myCleanup = lambda3` → 保存 lambda,**不执行** 5. **设置作用域属性**: - `scope.manager = this` → 关联管理器 - `scope.nodeAttached = node` → 直接设置节点(不需要 lambda) - `scope.nodeRef = node` → 设置节点引用 6. **调用 `param(0, lambda4, ...)`**: ```181:181:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj scope.param(0, {=> dumpHierarchyOf(scope)}, CONTEXT_ROOT_SCOPE_HIERARCHY_SUPPLIER, true) ``` - **创建 lambda4**: `{=> dumpHierarchyOf(scope)}` - **注意**: lambda4 被传入 `param`,但**此时不执行** - 在 `param` 中创建 `ParameterImpl`,lambda4 被保存为参数值 7. **调用 `param(1, lambda5, ...)`**: ```182:182:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj scope.param(1, {=> dumpHierarchyOf(node)}, CONTEXT_ROOT_NODE_HIERARCHY_SUPPLIER, true) ``` - **创建 lambda5**: `{=> dumpHierarchyOf(node)}` - **注意**: lambda5 被传入 `param`,但**此时不执行** 8. **返回 `scope`**(根作用域) **关键点**: - 所有 lambda 在此阶段只是被创建和保存,**均未执行** - `recomputeNeeded = true`(默认值,首次需要计算) --- ### 1.3 param 函数执行细节 ```220:238:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj public func param<Value>(index: Int64, value: Value, name: ?String, contextLocal: Bool): State<Value> { let params = this.params.getOrThrow() let manager = this.manager.getOrThrow() if (let Some(param) <- params[index]) { let state = (param as ParameterImpl<Value>).getOrThrow() if (contextLocal && !this.hasNamedState(name.getOrThrow(), params[index])) { throw IllegalArgumentException("name was unexpectedly changed to " + name.getOrThrow()) } state.setValue(value) return state } else { let state = ParameterImpl<Value>(value, name, manager) params[index] = state if (contextLocal) { this.setNamedState(name.getOrThrow(), state) } return state } } ``` **执行内容**(首次调用,`params[index]` 为 `None`): 1. 创建 `ParameterImpl<Value>(value, name, manager)` - `value` 可能是 lambda(如 `{=> dumpHierarchyOf(scope)}`) - **注意**: lambda 作为值被保存,**不执行** 2. `params[index] = state` → 保存参数状态 3. 返回 `State<Value>` --- ## 2. getValue 触发时的完整流程 ### 2.1 调用链概览 ``` 用户代码: rootScope.getValue() 或 访问 .value ↓ ScopeImpl.getValue() ↓ ScopeImpl.isUnchanged() ↓ (如果是首次/需要重算) 设置 manager.currentScope = this ↓ ScopeImpl.isUnchanged() 返回 false ↓ ScopeImpl.getValue() 继续 ↓ 执行 lambda2: myCompute() [即 { => update(); node }] ↓ ├─ 执行 lambda1: manager.runWithFrozen(update) ↓ ├─ frozen = true ↓ ├─ 执行用户传入的 update(manager) [真正的用户代码] ↓ └─ frozen = old └─ 返回 node ↓ ScopeImpl.recache(node) ↓ ├─ 执行 param(0).getValue() → 执行 lambda4: dumpHierarchyOf(scope) ├─ 执行 param(1).getValue() → 执行 lambda5: dumpHierarchyOf(node) ├─ detachChildScopes(None) ├─ parent?.increment(...) └─ nodeAttached?.incrementalUpdateDone(...) ↓ ScopeImpl.getCached() ↓ 返回 node ``` ### 2.2 逐步执行分析 #### 步骤 1: getValue 被调用 ```371:378:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj public func getValue(): Value { if (this.isUnchanged()) { this.getCached() } else { let compute = this.myCompute.getOrThrow() this.recache(compute()) } } ``` **执行内容**: 1. 调用 `isUnchanged()` → 判断是否需要重新计算 --- #### 步骤 2: isUnchanged 被调用(首次/需要重算) ```384:406:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj public func isUnchanged(): Bool { if (this.isRecomputeNeeded()) { this.incremental = None this.nodeCount = 0 if (!this.isDisposed()) { if(let Some(manager) <- this.manager) { this.scopeInternal = manager.currentScope // Record scopeInternal for recache manager.currentScope = this } } return false } else { this.parent?.increment( if (this.nodeAttached.isSome()) { 1 } else { this.nodeCount }, true ) return true } } ``` **执行内容**(首次调用,`recomputeNeeded = true`): 1. `isRecomputeNeeded()` → 返回 `true` 2. `this.incremental = None` → 清空增量引用 3. `this.nodeCount = 0` → 重置节点计数 4. **保存当前作用域**: `this.scopeInternal = manager.currentScope` 5. **切换当前作用域**: `manager.currentScope = this` - **关键**: 此时后续的状态访问会在此作用域下登记依赖 6. **返回 `false`** → 表示需要重新计算 --- #### 步骤 3: getValue 继续执行(计算分支) ```371:378:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj public func getValue(): Value { if (this.isUnchanged()) { this.getCached() } else { let compute = this.myCompute.getOrThrow() // 获取 lambda2 this.recache(compute()) // 执行 lambda2 } } ``` **执行内容**: 1. `this.myCompute.getOrThrow()` → 获取保存的 lambda2: `{ => update(); node }` 2. **执行 lambda2**: `compute()` - **此时才真正执行 lambda2** --- #### 步骤 4: lambda2 执行(lambda1 被调用) lambda2 的内容: ```170:174:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj { => update() // 这是 lambda1: { => manager.runWithFrozen(update) } node } ``` **执行内容**: 1. **执行 `update()`** → 这是 lambda1: `{ => manager.runWithFrozen(update) }` - **此时才真正执行 lambda1** --- #### 步骤 5: lambda1 执行(runWithFrozen) lambda1 的内容: ```55:60:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj func runWithFrozen(runnable: (StateManagerImpl) -> Unit): Unit { let old = frozen frozen = true // states are frozen during recomposition runnable(this) // 执行用户传入的 update(manager) frozen = old } ``` **执行内容**: 1. `let old = frozen` → 保存旧的冻结状态(通常是 `false`) 2. `frozen = true` → 设置冻结标志(防止状态在重组期间被修改) 3. **执行 `runnable(this)`** → 这是用户传入的 `update(manager)` - **此时才执行用户的 update 函数** - 在 `update` 中,用户代码可能调用 `memo`、`NodeAttach` 等,这些会创建子作用域并可能触发计算 4. `frozen = old` → 恢复冻结状态 5. **返回**(lambda1 执行完成) --- #### 步骤 6: lambda2 继续(返回 node) lambda2 继续执行: ``` update() // 已完成 node // 返回 node ``` **返回**: `node`(根节点) --- #### 步骤 7: recache 被调用 ```416:438:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj public func recache(newValue: Value): Value { if (!this.isDisposed()) { if(let Some(manager) <- this.manager) { manager.currentScope = this.scopeInternal // 恢复之前的作用域 } } let oldValue = this.myValue this.myValue = newValue this.myModified = this.myComputed && !equalValues(newValue, oldValue.getOrThrow()) this.myComputed = true this.recomputeNeeded = false this.detachChildScopes(None) this.parent?.increment( if (this.nodeAttached.isSome()) { 1 } else { this.nodeCount }, false ) this.nodeAttached?.incrementalUpdateDone(this.parent?.nodeRef ?? None) return this.getCached() } ``` **执行内容**: 1. **恢复作用域**: `manager.currentScope = this.scopeInternal` - 恢复到进入计算前的状态 2. **更新缓存值**: - `this.myValue = newValue` → 保存计算结果(node) - `this.myComputed = true` → 标记已计算 - `this.recomputeNeeded = false` → 标记不需要重算 3. `detachChildScopes(None)` → 断开失效的子作用域 4. `this.parent?.increment(...)` → 通知父作用域更新节点计数 5. `this.nodeAttached?.incrementalUpdateDone(...)` → 节点增量更新完成 6. **调用 `getCached()`** → 获取缓存值并可能触发参数 lambda 执行 --- #### 步骤 8: getCached 被调用 ```408:414:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj public func getCached(): Value { if(let Some(dep) <- this.manager?.getDependency()) { this.dependencies?.register(dep) } this.dependencies?.onUpdate(this.myModified) this.myValue.getOrThrow() } ``` **执行内容**: 1. 登记依赖(如果有当前依赖) 2. `this.dependencies?.onUpdate(this.myModified)` → 更新依赖关系 3. **返回 `this.myValue.getOrThrow()`** → 返回缓存的 node **注意**: 在根作用域中,参数(param(0) 和 param(1))的 lambda(lambda4 和 lambda5)可能在以下情况被执行: - 当参数被访问时(如 `param(0).getValue()`) - 在依赖更新或其他需要评估参数值时 --- ### 2.3 子作用域创建流程(在 update 中) 当用户的 `update` 函数执行时,可能调用 `memo` 或 `NodeAttach`,这些会创建子作用域。 #### 示例: memo2 调用流程 ```48:56:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/memo.cj func memo2<P1, P2, Value>(p1: P1, p2: P2, compute: (__memo_key: Hashscopeid, manager: StateManagerImpl, s1: State<P1>, s2: State<P2>) -> Value): Value { let scope = manager.getMemoScope<Value>(_new_id, 2, None, None, None, false, None) let s1 = scope.param(0, p1) let s2 = scope.param(1, p2) if (scope.isUnchanged()) { scope.getCached() } else { scope.recache(compute(s1, s2)) } } ``` **执行内容**: 1. `manager.getMemoScope<Value>(...)` → 获取或创建子作用域 - 调用 `getChildScope`,如果未找到则创建新的 `ScopeImpl` - **注意**: `compute` 参数为 `None`,此时不传入计算函数 2. `scope.param(0, p1)` → 创建参数状态 - 创建 `ParameterImpl`,保存 `p1` 值 - **注意**: `p1` 可能是值,也可能是 lambda,但**不执行** 3. `scope.param(1, p2)` → 创建参数状态 4. `scope.isUnchanged()` → 判断是否需要重算 - 首次调用返回 `false`(需要重算) 5. **执行 `compute(s1, s2)`** → 这是用户传入的计算函数 - **此时才真正执行用户的 compute lambda** - `s1` 和 `s2` 是 `State` 对象,访问时会触发依赖登记 6. `scope.recache(compute(s1, s2))` → 缓存计算结果 --- ## 3. Lambda 表达式执行时机总结 ### 3.1 创建阶段(不执行) 以下 lambda 在创建阶段被创建和保存,**但不执行**: | Lambda | 创建位置 | 保存位置 | 执行时机 | |--------|---------|---------|---------| | `lambda1`: `{ => manager.runWithFrozen(update) }` | `memoRoot` | `updatableNode` 的 `update` 参数 | `getValue()` 时 | | `lambda2`: `{ => update(); node }` | `updatableNode` | `ScopeImpl.myCompute` | `getValue()` → `compute()` 时 | | `lambda3`: `{_ => cleanup?()}` | `updatableNode` | `ScopeImpl.myCleanup` | 作用域销毁时 | | `lambda4`: `{=> dumpHierarchyOf(scope)}` | `updatableNode` | `ParameterImpl.value` | 参数被访问时 | | `lambda5`: `{=> dumpHierarchyOf(node)}` | `updatableNode` | `ParameterImpl.value` | 参数被访问时 | | `用户 update`: `(manager) => { ... }` | 用户代码 | 捕获在 lambda1 中 | lambda1 执行时 | | `用户 compute`: `(key, mgr, s1, s2) => { ... }` | 用户代码 | 传入 `memo2` 等 | `memo2` 中调用时 | ### 3.2 执行时机总结 | 执行阶段 | 触发的 Lambda | 调用路径 | |---------|-------------|---------| | **创建阶段** | 无 | 所有 lambda 只是被创建和保存 | | **getValue() 触发** | lambda2 (`myCompute`) | `getValue()` → `compute()` | | **lambda2 执行** | lambda1 (`runWithFrozen`) | `lambda2` → `update()` | | **lambda1 执行** | 用户 `update` | `lambda1` → `runnable(this)` | | **用户 update 中** | `memo`/`NodeAttach` 的 compute | `memo2` → `compute(s1, s2)` | | **参数访问** | 参数 lambda(如 lambda4/lambda5) | `param.getValue()` 时 | ### 3.3 关键原则 1. **延迟执行**: Lambda 在创建时不执行,只在需要时才执行 2. **作用域切换**: 在执行 lambda 前,`manager.currentScope` 被设置为当前作用域 3. **冻结保护**: 在重组期间(`frozen=true`),状态修改被禁止 4. **依赖登记**: 在 lambda 执行期间访问状态时,自动登记依赖关系 --- ## 4. 完整示例:创建到首次计算的完整流程 ### 场景设置 ```text let counter = mutableState(0) let rootNode = IncrementalNode() let rootScope = memoRoot(rootNode, (manager) => { // 组件A NodeAttach( () => IncrementalNode(), (nodeA) => { let c = counter.getValue() println("A: ${c}") } ) }) ``` ### 完整调用链 ``` 1. memoRoot(rootNode, update) ├─ GlobalStateManager.instance() → 返回 manager ├─ 创建 lambda1: { => manager.runWithFrozen(update) } └─ updatableNode(rootNode, lambda1, None) ├─ 创建 lambda2: { => update(); node } ├─ 创建 lambda3: {_ => cleanup?()} ├─ ScopeImpl.init(None, 2, lambda2, lambda3, None) │ ├─ super(None, 2) │ ├─ this.myCompute = lambda2 [保存,不执行] │ └─ this.myCleanup = lambda3 [保存,不执行] ├─ scope.param(0, lambda4, ...) [lambda4 保存,不执行] ├─ scope.param(1, lambda5, ...) [lambda5 保存,不执行] └─ 返回 scope [根作用域创建完成] 2. 首次访问 rootScope.getValue() ├─ ScopeImpl.getValue() │ ├─ isUnchanged() │ │ ├─ isRecomputeNeeded() → true │ │ ├─ this.scopeInternal = manager.currentScope [保存] │ │ ├─ manager.currentScope = this [切换] │ │ └─ 返回 false │ ├─ compute = this.myCompute.getOrThrow() [获取 lambda2] │ └─ recache(compute()) [执行 lambda2] │ ├─ 执行 lambda2: { => update(); node } │ │ └─ 执行 update() [即 lambda1] │ │ ├─ frozen = true │ │ ├─ 执行 runnable(this) [即用户 update] │ │ │ └─ NodeAttach(createA, updateA) │ │ │ ├─ getMemoScope(...) [创建子作用域] │ │ │ ├─ createA() → 创建 nodeA [执行 create lambda] │ │ │ ├─ updateA(nodeA) [执行 update lambda] │ │ │ │ └─ counter.getValue() │ │ │ │ └─ counter.onAccess() [登记依赖] │ │ │ └─ recache(nodeA) │ │ └─ frozen = old │ │ └─ 返回 node │ ├─ manager.currentScope = this.scopeInternal [恢复] │ ├─ this.myValue = node │ ├─ this.recomputeNeeded = false │ ├─ detachChildScopes(None) │ ├─ parent?.increment(...) │ ├─ nodeAttached?.incrementalUpdateDone(...) │ └─ getCached() │ └─ 返回 node └─ 返回 node ```

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PoivronMax/idlize-cj-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server